package cn.ekuma.android.ui.scan;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import cn.ekuma.android.ui.InputView;
import cn.ekuma.android.ui.InputView.OnSelectedActionListener;
import cn.ekuma.android.ui.scan.camera.CameraManager;
import cn.ekuma.android.ui.scan.history.HistoryActivity;
import cn.ekuma.android.ui.scan.history.HistoryManager;
import cn.ekuma.android.ui.scan.result.ResultButtonListener;
import cn.ekuma.android.ui.scan.result.ResultHandler;
import cn.ekuma.android.ui.scan.result.ResultHandlerFactory;
import cn.ekuma.epos.android.Config;
import cn.ekuma.epos.android.finder.ProductDialyActivate;
import cn.ekuma.epos.datalogic.DataLogicPDA;
import cn.ekuma.epos.datalogic.I_DataLogicSales;
import cn.ekuma.epos.datalogic.local.LocalDBHelper;
import cn.ekuma.epos.datalogic.local.dao.ProductDAO;
import cn.ekuma.epos.datalogic.util.ProductUtil;
import cn.ekuma.epos.ui.android.R;

import com.caucho.hessian.client.HessianRuntimeException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.openbravo.bean.Product;
import com.openbravo.data.basic.BasicException;

public abstract class CaptureScanActivity extends Activity implements SurfaceHolder.Callback, View.OnLongClickListener {

	  private static final String TAG = CaptureScanActivity.class.getSimpleName();

	  protected static final int RESERT_ID = Menu.FIRST;
    protected static final int HISTORY_ID = Menu.FIRST +1;
    protected static final int SETTINGS_ID = Menu.FIRST + 2;
    protected static final int UPDATE_LOCAL_ID = Menu.FIRST + 3;
    protected static final int ADVCODEINPUT_ID = Menu.FIRST+4;



    protected static final long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
    protected static final long BULK_MODE_SCAN_DELAY_MS = 1000L;

	  public static final String SELECTED_BEANS="selected_beans";

	  public static final int HISTORY_REQUEST_CODE = 0x0000bacc;
	  
	  private static final int SCAN_PRODUCTS_REQUEST_CODE= 0x0000cacc;
	  
	  private static final Set<ResultMetadataType> DISPLAYABLE_METADATA_TYPES =
	      EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
	                 ResultMetadataType.SUGGESTED_PRICE,
	                 ResultMetadataType.ERROR_CORRECTION_LEVEL,
	                 ResultMetadataType.POSSIBLE_COUNTRY);

     protected CameraManager cameraManager;
	  private CaptureActivityHandler handler;
	  private Result savedResultToShow;
	  protected ViewfinderView viewfinderView;
	  private TextView statusView;
	  private View resultView;
	  private Result lastResult;
	  private boolean hasSurface;
	  private IntentSource source;
	  private Collection<BarcodeFormat> decodeFormats;
	  private String characterSet;
      protected HistoryManager historyManager;
      protected BeepManager beepManager;
	  InputView inputView;
      protected boolean isLocalModel;
      protected LocalDBHelper localDbHelper;
      protected ProductDAO productDAO;

	  protected SharedPreferences prefs;
      protected boolean isLotNumberModel;
       protected String matchPattern;
	  
	  ViewfinderView getViewfinderView() {
	    return viewfinderView;
	  }

	  public Handler getHandler() {
	    return handler;
	  }

	  CameraManager getCameraManager() {
	    return cameraManager;
	  }

	  public void onCreate(Bundle icicle) {
	    super.onCreate(icicle);
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
	    Window window = getWindow();
	    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
	    setContentView(R.layout.capture);

	    hasSurface = false;
	    historyManager = new HistoryManager(this);
	    clearHistory(historyManager);
	    beepManager = new BeepManager(this);

	    localDbHelper=new LocalDBHelper(this);
	    productDAO=new ProductDAO(localDbHelper);
 
	    //PreferenceManager.setDefaultValues(this, Intents.PREFS_NAME, 0, R.xml.preferences, false);//.setDefaultValues(this, R.xml.preferences, false);

	    inputView=(InputView) findViewById(R.id.input_view);
	    inputView.setOnSelectedActionListener(new OnSelectedActionListener(){
			public void onOkAction(double value) {
				if(lastResult!=null)
				   historyManager.updateHistoryItemUnits(lastResult.getText(), value);
				restartPreviewAfterDelay(0L);
			}
	    });
	  }

	 
	protected void clearHistory(HistoryManager historyManager) {
		  historyManager.clearHistory();
	}

	protected void onResume() {
	    super.onResume();
	    // CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
	    // want to open the camera driver and measure the screen size if we're going to show the help on
	    // first launch. That led to bugs where the scanning rectangle was the wrong size and partially
	    // off screen.
	    cameraManager = new CameraManager(getApplication());

	    viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
	    viewfinderView.setCameraManager(cameraManager);
        viewfinderView.setOnLongClickListener(this);

	    resultView = findViewById(R.id.result_view);
	    statusView = (TextView) findViewById(R.id.status_view);
        statusView.setOnLongClickListener(this);

	    handler = null;
	    lastResult = null;

	    resetStatusView();

	    SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
	    SurfaceHolder surfaceHolder = surfaceView.getHolder();
	    if (hasSurface) {
	      // The activity was paused but not stopped, so the surface still exists. Therefore
	      // surfaceCreated() won't be called, so init the camera here.
	      initCamera(surfaceHolder);
	    } else {
	      // Install the callback and wait for surfaceCreated() to init the camera.
	      surfaceHolder.addCallback(this);
	      surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	    }

	    beepManager.updatePrefs();

	    Intent intent = getIntent();

	   

	    isLocalModel=prefs.getBoolean(Config.KEY_DB_LOCALMODEL, false);
        isLotNumberModel=prefs.getBoolean(Config.KEY_LOT_NUMBER,false);
        matchPattern=prefs.getString(Config.KEY_LOT_NUMBER_PATTERN,"\\d*");


	    source = IntentSource.NONE;
	    decodeFormats = null;
	    characterSet = null;

	    if (intent != null) {

	      String action = intent.getAction();
	      String dataString = intent.getDataString();

	      if (Intents.Scan.ACTION.equals(action)) {

	        // Scan the formats the intent requested, and return the result to the calling activity.
	        source = IntentSource.NATIVE_APP_INTENT;
	        decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);

	        if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
	          int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
	          int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
	          if (width > 0 && height > 0) {
	            cameraManager.setManualFramingRect(width, height);
	          }
	        }
	        
	        String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
	        if (customPromptMessage != null) {
	          statusView.setText(customPromptMessage);
	        }

	      }
	      
	      characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);

	    }
	  }
	  

	 
	  protected void onPause() {
	    if (handler != null) {
	      handler.quitSynchronously();
	      handler = null;
	    }
	    cameraManager.closeDriver();
	    if (!hasSurface) {
	      SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
	      SurfaceHolder surfaceHolder = surfaceView.getHolder();
	      surfaceHolder.removeCallback(this);
	    }
	    super.onPause();
	  }

	 
	  public boolean onKeyDown(int keyCode, KeyEvent event) {
	    if (keyCode == KeyEvent.KEYCODE_BACK) {
	      if (source == IntentSource.NATIVE_APP_INTENT) {
	        setResult(RESULT_CANCELED);
	        finish();
	        return true;
	      } else if ((source == IntentSource.NONE || source == IntentSource.ZXING_LINK) && lastResult != null) {
	        restartPreviewAfterDelay(0L);
	        return true;
	      }
	    } else if (keyCode == KeyEvent.KEYCODE_FOCUS || keyCode == KeyEvent.KEYCODE_CAMERA) {
	      // Handle these events so they don't launch the Camera app
	      return true;
	    }
	    return super.onKeyDown(keyCode, event);
	  }


	  
	  private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
	    // Bitmap isn't used yet -- will be used soon
	    if (handler == null) {
	      savedResultToShow = result;
	    } else {
	      if (result != null) {
	        savedResultToShow = result;
	      }
	      if (savedResultToShow != null) {
	        Message message = Message.obtain(handler,Intents.IDS.decode_succeeded, savedResultToShow);
	        handler.sendMessage(message);
	      }
	      savedResultToShow = null;
	    }
	  }

	 
	  public void surfaceCreated(SurfaceHolder holder) {
	    if (holder == null) {
	      Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
	    }
	    if (!hasSurface) {
	      hasSurface = true;
	      initCamera(holder);
	    }
	  }

	 
	  public void surfaceDestroyed(SurfaceHolder holder) {
	    hasSurface = false;
	  }

	 
	  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

	  }

	  /**
	   * A valid barcode has been found, so give an indication of success and show the results.
	   *
	   * @param rawResult The contents of the barcode.
	   * @param barcode   A greyscale bitmap of the camera data which was decoded.
	 * @param scaleFactor 
	   */
	  public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
	    lastResult = rawResult;
        if(isLotNumberModel){
            String codeOrReference=lastResult.getText();
            if(codeOrReference.matches(matchPattern))
                productHander.obtainMessage(ProductUtil.MSG_SUCCESS,codeOrReference).sendToTarget();
            else {
                Toast.makeText(CaptureScanActivity.this, "与设定规则不匹配，请检查\r\n" + codeOrReference, Toast.LENGTH_SHORT).show();
                restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
            }
            return;
        }
	    final ProgressDialog dialog = ProgressDialog.show(this, "", 
		        "查旬中，请稍等 …", true, true);
		new Thread(){
			@Override
			public void run(){
				String codeOrReference=lastResult.getText();
				System.out.println("code is:"+codeOrReference);
				ProductUtil.findProduct(codeOrReference, isLocalModel, productHander, productDAO, getProductBuilderType());
				dialog.dismiss(); 
			}			
			}.start();  
	  }
	  
	  
	  protected Handler productHander   = new Handler() {  
	        public void handleMessage (Message msg) {//此方法在ui线程运行   
	            switch(msg.what) {  
	            case ProductUtil.MSG_SUCCESS:  
	              String showSrc=(String) msg.obj;
	              beepManager.playBeepSoundAndVibrate();
	              ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(CaptureScanActivity.this, lastResult);
	              historyManager.addHistoryItem(lastResult, resultHandler,showSrc);	
	              //drawResultPoints(barcode, rawResult);
	      	      switch (source) {
	      	        case NATIVE_APP_INTENT:
	      	        case PRODUCT_SEARCH_LINK:
	      	          handleDecodeExternally(lastResult, resultHandler, null);
	      	          break;
	      	        case NONE:
	      	          if (prefs.getBoolean(Config.KEY_BULK_MODE, false)) {
	      	            Toast.makeText(CaptureScanActivity.this, getString(R.string.msg_bulk_mode_scanned)+":\r\n"+showSrc, Toast.LENGTH_SHORT).show();
	      	            // Wait a moment or else it will scan the same barcode continuously about 3 times
	      	            restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
	      	          } else {
	      	            handleDecodeInternally(lastResult, resultHandler, null,showSrc);
	      	          }
	      	          break;
	      	         }
					
	                break;  
	            case ProductUtil.MSG_FAILURE:  
	            	Toast.makeText(getBaseContext(), getString(R.string.msg_no_find_product)+":"+lastResult.getText(), Toast.LENGTH_SHORT).show();
	            	// Wait a moment or else it will scan the same barcode continuously about 3 times
	            	restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
	            	break;
	            }  
	        }  
	    };
	  
	  
	  
	  public int getProductBuilderType(){
		  return ProductUtil.BUILDERTYPE_BASE;
	  }
	  
	  
	  /**
	   * Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
	   *
	   * @param barcode   A bitmap of the captured image.
	   * @param rawResult The decoded results which contains the points to draw.
	   */
	  private void drawResultPoints(Bitmap barcode, Result rawResult) {
	    ResultPoint[] points = rawResult.getResultPoints();
	    if (points != null && points.length > 0) {
	      Canvas canvas = new Canvas(barcode);
	      Paint paint = new Paint();
	      paint.setColor(getResources().getColor(R.color.result_image_border));
	      paint.setStrokeWidth(3.0f);
	      paint.setStyle(Paint.Style.STROKE);
	      Rect border = new Rect(2, 2, barcode.getWidth() - 2, barcode.getHeight() - 2);
	      canvas.drawRect(border, paint);

	      paint.setColor(getResources().getColor(R.color.result_points));
	      if (points.length == 2) {
	        paint.setStrokeWidth(4.0f);
	        drawLine(canvas, paint, points[0], points[1]);
	      } else if (points.length == 4 &&
	                 (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
	                  rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
	        // Hacky special case -- draw two lines, for the barcode and metadata
	        drawLine(canvas, paint, points[0], points[1]);
	        drawLine(canvas, paint, points[2], points[3]);
	      } else {
	        paint.setStrokeWidth(10.0f);
	        for (ResultPoint point : points) {
	          canvas.drawPoint(point.getX(), point.getY(), paint);
	        }
	      }
	    }
	  }

	  private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b) {
	    canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), paint);
	  }

	  // Put up our own UI for how to handle the decoded contents.
	  private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode,String displayContents) {
	    statusView.setVisibility(View.GONE);
	    viewfinderView.setVisibility(View.GONE);
	    resultView.setVisibility(View.VISIBLE);
	    inputView.reset(1);
	    
	    TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
	    contentsTextView.setText(displayContents);

	    // Crudely scale betweeen 22 and 32 -- bigger font for shorter text
	   // int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
	   // contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);

	    int buttonCount = resultHandler.getButtonCount();
	    ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
	    buttonView.requestFocus();
	    for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
	      TextView button = (TextView) buttonView.getChildAt(x);
	      if (x < buttonCount) {
	        button.setVisibility(View.VISIBLE);
	        button.setText(resultHandler.getButtonText(x));
	        button.setOnClickListener(new ResultButtonListener(resultHandler, x));
	      } else {
	        button.setVisibility(View.GONE);
	      }
	    }
	  }

	  // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
	  private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
	    viewfinderView.drawResultBitmap(barcode);

	    // Since this message will only be shown for a second, just tell the user what kind of
	    // barcode was found (e.g. contact info) rather than the full contents, which they won't
	    // have time to read.
	    statusView.setText(getString(resultHandler.getDisplayTitle()));

	    if (source == IntentSource.NATIVE_APP_INTENT) {
	      
	      // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
	      // the deprecated intent is retired.
	      Intent intent = new Intent(getIntent().getAction());
	      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
	      intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
	      intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
	      byte[] rawBytes = rawResult.getRawBytes();
	      if (rawBytes != null && rawBytes.length > 0) {
	        intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
	      }
	      Map<ResultMetadataType,?> metadata = rawResult.getResultMetadata();
	      if (metadata != null) {
	        Integer orientation = (Integer) metadata.get(ResultMetadataType.ORIENTATION);
	        if (orientation != null) {
	          intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
	        }
	        String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
	        if (ecLevel != null) {
	          intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
	        }
	        Iterable<byte[]> byteSegments = (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);
	        if (byteSegments != null) {
	          int i = 0;
	          for (byte[] byteSegment : byteSegments) {
	            intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);
	            i++;
	          }
	        }
	      }
	      sendReplyMessage(Intents.IDS.return_scan_result, intent);
	    }  
	  }
	  
	  private void sendReplyMessage(int id, Object arg) {
	    Message message = Message.obtain(handler, id, arg);
	    long resultDurationMS = getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
	                                                     DEFAULT_INTENT_RESULT_DURATION_MS);
	    if (resultDurationMS > 0L) {
	      handler.sendMessageDelayed(message, resultDurationMS);
	    } else {
	      handler.sendMessage(message);
	    }
	  }
	  
	  private Map<DecodeHintType,?> decodeHints;
	  
	  private void initCamera(SurfaceHolder surfaceHolder) {
	    try {
	      cameraManager.openDriver(surfaceHolder);
	      // Creating the handler starts the preview, which can also throw a RuntimeException.
	      if (handler == null) {
	        handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
	      }
	      decodeOrStoreSavedBitmap(null, null);
	    } catch (IOException ioe) {
	      Log.w(TAG, ioe);
	      displayFrameworkBugMessageAndExit();
	    } catch (RuntimeException e) {
	      // Barcode Scanner has seen crashes in the wild of this variety:
	      // java.?lang.?RuntimeException: Fail to connect to camera service
	      Log.w(TAG, "Unexpected error initializing camera", e);
	      displayFrameworkBugMessageAndExit();
	    }
	  }

	  private void displayFrameworkBugMessageAndExit() {
	    AlertDialog.Builder builder = new AlertDialog.Builder(this);
	    builder.setTitle(getString(R.string.app_name));
	    builder.setMessage(getString(R.string.msg_camera_framework_bug));
	    builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
	    builder.setOnCancelListener(new FinishListener(this));
	    builder.show();
	  }

	  public void restartPreviewAfterDelay(long delayMS) {
	    if (handler != null) {
	      handler.sendEmptyMessageDelayed(Intents.IDS.restart_preview, delayMS);
	    }
	    resetStatusView();
	  }

	  private void resetStatusView() {
	    resultView.setVisibility(View.GONE);
	    statusView.setText(R.string.msg_default_status);
	    statusView.setVisibility(View.VISIBLE);
	    viewfinderView.setVisibility(View.VISIBLE);
	    lastResult = null;
	  }

	  public void drawViewfinder() {
	    viewfinderView.drawViewfinder();
	  }

    @Override
    public  boolean onLongClick(View v) {
        return onFinderViewLongClicked();
    }

    abstract boolean onFinderViewLongClicked();
}
